{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# TensorFlow2.0教程-使用keras训练模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "# !pip install -q pydot\n",
    "# !apt-get install graphviz"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "from __future__ import absolute_import, division, print_function\n",
    "import tensorflow as tf\n",
    "tf.keras.backend.clear_session()\n",
    "import tensorflow.keras as keras\n",
    "import tensorflow.keras.layers as layers"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1.一般的模型构造、训练、测试流程"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Train on 50000 samples, validate on 10000 samples\n",
      "Epoch 1/3\n",
      "50000/50000 [==============================] - 2s 33us/sample - loss: 0.3454 - sparse_categorical_accuracy: 0.9005 - val_loss: 0.1787 - val_sparse_categorical_accuracy: 0.9482\n",
      "Epoch 2/3\n",
      "50000/50000 [==============================] - 1s 27us/sample - loss: 0.1575 - sparse_categorical_accuracy: 0.9529 - val_loss: 0.1412 - val_sparse_categorical_accuracy: 0.9585\n",
      "Epoch 3/3\n",
      "50000/50000 [==============================] - 1s 28us/sample - loss: 0.1169 - sparse_categorical_accuracy: 0.9650 - val_loss: 0.1235 - val_sparse_categorical_accuracy: 0.9648\n",
      "history:\n",
      "{'loss': [0.3453604544830322, 0.1575087631160021, 0.11689146610498428], 'sparse_categorical_accuracy': [0.90052, 0.95286, 0.96496], 'val_loss': [0.17870783286094666, 0.14119704793691634, 0.12352603247761726], 'val_sparse_categorical_accuracy': [0.9482, 0.9585, 0.9648]}\n",
      "10000/10000 [==============================] - 0s 9us/sample - loss: 0.1278 - sparse_categorical_accuracy: 0.9620\n",
      "evaluate:\n",
      "[0.1278399595953524, 0.962]\n",
      "predict:\n",
      "[[4.2855617e-09 1.0494307e-06 3.0937063e-05 7.9090823e-06 2.0066829e-10\n",
      "  1.1333327e-08 1.6852811e-13 9.9995875e-01 8.7579053e-08 1.1900718e-06]\n",
      " [3.8077911e-07 8.7324406e-06 9.9991751e-01 5.1405259e-05 1.7370488e-09\n",
      "  3.8539181e-07 1.4430083e-06 2.5448646e-08 2.0086347e-05 5.0038074e-10]]\n"
     ]
    }
   ],
   "source": [
    "\n",
    "\n",
    "# 模型构造\n",
    "inputs = keras.Input(shape=(784,), name='mnist_input')\n",
    "h1 = layers.Dense(64, activation='relu')(inputs)\n",
    "h1 = layers.Dense(64, activation='relu')(h1)\n",
    "outputs = layers.Dense(10, activation='softmax')(h1)\n",
    "model = keras.Model(inputs, outputs)\n",
    "# keras.utils.plot_model(model, 'net001.png', show_shapes=True)\n",
    "\n",
    "model.compile(optimizer=keras.optimizers.RMSprop(),\n",
    "             loss=keras.losses.SparseCategoricalCrossentropy(),\n",
    "             metrics=[keras.metrics.SparseCategoricalAccuracy()])\n",
    "\n",
    "# 载入数据\n",
    "(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()\n",
    "x_train = x_train.reshape(60000, 784).astype('float32') /255\n",
    "x_test = x_test.reshape(10000, 784).astype('float32') /255\n",
    "\n",
    "x_val = x_train[-10000:]\n",
    "y_val = y_train[-10000:]\n",
    "\n",
    "x_train = x_train[:-10000]\n",
    "y_train = y_train[:-10000]\n",
    "\n",
    "# 训练模型\n",
    "history = model.fit(x_train, y_train, batch_size=64, epochs=3,\n",
    "         validation_data=(x_val, y_val))\n",
    "print('history:')\n",
    "print(history.history)\n",
    "\n",
    "result = model.evaluate(x_test, y_test, batch_size=128)\n",
    "print('evaluate:')\n",
    "print(result)\n",
    "pred = model.predict(x_test[:2])\n",
    "print('predict:')\n",
    "print(pred)\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2.自定义损失和指标\n",
    "自定义指标只需继承Metric类， 并重写一下函数\n",
    "\n",
    "\\__init\\__(self)，初始化。\n",
    "\n",
    "update_state(self，y_true，y_pred，sample_weight = None)，它使用目标y_true和模型预测y_pred来更新状态变量。\n",
    "\n",
    "result(self)，它使用状态变量来计算最终结果。\n",
    "\n",
    "reset_states(self)，重新初始化度量的状态。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/3\n",
      "50000/50000 [==============================] - 1s 27us/sample - loss: 0.0590 - binary_true_postives: 9186.0000\n",
      "Epoch 2/3\n",
      "50000/50000 [==============================] - 1s 24us/sample - loss: 0.0499 - binary_true_postives: 9957.0000\n",
      "Epoch 3/3\n",
      "50000/50000 [==============================] - 1s 25us/sample - loss: 0.0441 - binary_true_postives: 10939.0000\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<tensorflow.python.keras.callbacks.History at 0x7f7b88f8fb38>"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 这是一个简单的示例，显示如何实现CatgoricalTruePositives指标，该指标计算正确分类为属于给定类的样本数量\n",
    "\n",
    "class CatgoricalTruePostives(keras.metrics.Metric):\n",
    "    def __init__(self, name='binary_true_postives', **kwargs):\n",
    "        super(CatgoricalTruePostives, self).__init__(name=name, **kwargs)\n",
    "        self.true_postives = self.add_weight(name='tp', initializer='zeros')\n",
    "        \n",
    "    def update_state(self, y_true, y_pred, sample_weight=None):\n",
    "        y_pred = tf.argmax(y_pred)\n",
    "        y_true = tf.equal(tf.cast(y_pred, tf.int32), tf.cast(y_true, tf.int32))\n",
    "        \n",
    "        y_true = tf.cast(y_true, tf.float32)\n",
    "        \n",
    "        if sample_weight is not None:\n",
    "            sample_weight = tf.cast(sample_weight, tf.float32)\n",
    "            y_true = tf.multiply(sample_weight, y_true)\n",
    "            \n",
    "        return self.true_postives.assign_add(tf.reduce_sum(y_true))\n",
    "    \n",
    "    def result(self):\n",
    "        return tf.identity(self.true_postives)\n",
    "    \n",
    "    def reset_states(self):\n",
    "        self.true_postives.assign(0.)\n",
    "        \n",
    "\n",
    "model.compile(optimizer=keras.optimizers.RMSprop(1e-3),\n",
    "             loss=keras.losses.SparseCategoricalCrossentropy(),\n",
    "             metrics=[CatgoricalTruePostives()])\n",
    "\n",
    "model.fit(x_train, y_train,\n",
    "         batch_size=64, epochs=3)\n",
    "            \n",
    "            \n",
    "            "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "50000/50000 [==============================] - 2s 44us/sample - loss: 2.3663 - sparse_categorical_accuracy: 0.1135\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<tensorflow.python.keras.callbacks.History at 0x7f7b92fc50f0>"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 以定义网络层的方式添加网络loss\n",
    "class ActivityRegularizationLayer(layers.Layer):\n",
    "    def call(self, inputs):\n",
    "        self.add_loss(tf.reduce_sum(inputs) * 0.1)\n",
    "        return inputs\n",
    "\n",
    "inputs = keras.Input(shape=(784,), name='mnist_input')\n",
    "h1 = layers.Dense(64, activation='relu')(inputs)\n",
    "h1 = ActivityRegularizationLayer()(h1)\n",
    "h1 = layers.Dense(64, activation='relu')(h1)\n",
    "outputs = layers.Dense(10, activation='softmax')(h1)\n",
    "model = keras.Model(inputs, outputs)\n",
    "# keras.utils.plot_model(model, 'net001.png', show_shapes=True)\n",
    "\n",
    "model.compile(optimizer=keras.optimizers.RMSprop(),\n",
    "             loss=keras.losses.SparseCategoricalCrossentropy(),\n",
    "             metrics=[keras.metrics.SparseCategoricalAccuracy()])\n",
    "model.fit(x_train, y_train, batch_size=32, epochs=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "50000/50000 [==============================] - 2s 47us/sample - loss: 0.3014 - sparse_categorical_accuracy: 0.9104 - std_of_activation: 0.9998\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<tensorflow.python.keras.callbacks.History at 0x7f7b7f80eb70>"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 也可以以定义网络层的方式添加要统计的metric\n",
    "class MetricLoggingLayer(layers.Layer):\n",
    "    def call(self, inputs):\n",
    "        self.add_metric(keras.backend.std(inputs),\n",
    "                       name='std_of_activation',\n",
    "                       aggregation='mean')\n",
    "        \n",
    "        return inputs\n",
    "\n",
    "inputs = keras.Input(shape=(784,), name='mnist_input')\n",
    "h1 = layers.Dense(64, activation='relu')(inputs)\n",
    "h1 = MetricLoggingLayer()(h1)\n",
    "h1 = layers.Dense(64, activation='relu')(h1)\n",
    "outputs = layers.Dense(10, activation='softmax')(h1)\n",
    "model = keras.Model(inputs, outputs)\n",
    "# keras.utils.plot_model(model, 'net001.png', show_shapes=True)\n",
    "\n",
    "model.compile(optimizer=keras.optimizers.RMSprop(),\n",
    "             loss=keras.losses.SparseCategoricalCrossentropy(),\n",
    "             metrics=[keras.metrics.SparseCategoricalAccuracy()])\n",
    "model.fit(x_train, y_train, batch_size=32, epochs=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "50000/50000 [==============================] - 2s 46us/sample - loss: 2.3472 - sparse_categorical_accuracy: 0.1128 - std_of_activation: 0.3083\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<tensorflow.python.keras.callbacks.History at 0x7f7b7f416cc0>"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 也可以直接在model上面加\n",
    "# 也可以以定义网络层的方式添加要统计的metric\n",
    "class MetricLoggingLayer(layers.Layer):\n",
    "    def call(self, inputs):\n",
    "        self.add_metric(keras.backend.std(inputs),\n",
    "                       name='std_of_activation',\n",
    "                       aggregation='mean')\n",
    "        \n",
    "        return inputs\n",
    "\n",
    "inputs = keras.Input(shape=(784,), name='mnist_input')\n",
    "h1 = layers.Dense(64, activation='relu')(inputs)\n",
    "h2 = layers.Dense(64, activation='relu')(h1)\n",
    "outputs = layers.Dense(10, activation='softmax')(h2)\n",
    "model = keras.Model(inputs, outputs)\n",
    "\n",
    "model.add_metric(keras.backend.std(inputs),\n",
    "                       name='std_of_activation',\n",
    "                       aggregation='mean')\n",
    "model.add_loss(tf.reduce_sum(h1)*0.1)\n",
    "\n",
    "# keras.utils.plot_model(model, 'net001.png', show_shapes=True)\n",
    "\n",
    "model.compile(optimizer=keras.optimizers.RMSprop(),\n",
    "             loss=keras.losses.SparseCategoricalCrossentropy(),\n",
    "             metrics=[keras.metrics.SparseCategoricalAccuracy()])\n",
    "model.fit(x_train, y_train, batch_size=32, epochs=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "处理使用validation_data传入测试数据，还可以使用validation_split划分验证数据\n",
    "\n",
    "ps:validation_split只能在用numpy数据训练的情况下使用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Train on 40000 samples, validate on 10000 samples\n",
      "40000/40000 [==============================] - 2s 55us/sample - loss: 2.3012 - sparse_categorical_accuracy: 0.1141 - std_of_activation: 0.3087 - val_loss: 2.3016 - val_sparse_categorical_accuracy: 0.1115 - val_std_of_activation: 0.3065\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<tensorflow.python.keras.callbacks.History at 0x7f7b8086eeb8>"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "\n",
    "\n",
    "model.fit(x_train, y_train, batch_size=32, epochs=1, validation_split=0.2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3.使用tf.data构造数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/3\n",
      "100/100 [==============================] - 1s 10ms/step - loss: 0.7865 - sparse_categorical_accuracy: 0.7986 - val_loss: 0.3783 - val_sparse_categorical_accuracy: 0.8969\n",
      "Epoch 2/3\n",
      "100/100 [==============================] - 1s 5ms/step - loss: 0.3660 - sparse_categorical_accuracy: 0.8963 - val_loss: 0.3103 - val_sparse_categorical_accuracy: 0.9076\n",
      "Epoch 3/3\n",
      "100/100 [==============================] - 1s 5ms/step - loss: 0.3195 - sparse_categorical_accuracy: 0.9036 - val_loss: 0.2533 - val_sparse_categorical_accuracy: 0.9226\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<tensorflow.python.keras.callbacks.History at 0x7f7b75d4a1d0>"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def get_compiled_model():\n",
    "    inputs = keras.Input(shape=(784,), name='mnist_input')\n",
    "    h1 = layers.Dense(64, activation='relu')(inputs)\n",
    "    h2 = layers.Dense(64, activation='relu')(h1)\n",
    "    outputs = layers.Dense(10, activation='softmax')(h2)\n",
    "    model = keras.Model(inputs, outputs)\n",
    "    model.compile(optimizer=keras.optimizers.RMSprop(),\n",
    "                 loss=keras.losses.SparseCategoricalCrossentropy(),\n",
    "                 metrics=[keras.metrics.SparseCategoricalAccuracy()])\n",
    "    return model\n",
    "model = get_compiled_model()\n",
    "train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))\n",
    "train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64)\n",
    "\n",
    "val_dataset = tf.data.Dataset.from_tensor_slices((x_val, y_val))\n",
    "val_dataset = val_dataset.batch(64)\n",
    "\n",
    "# model.fit(train_dataset, epochs=3)\n",
    "# steps_per_epoch 每个epoch只训练几步\n",
    "# validation_steps 每次验证，验证几步\n",
    "model.fit(train_dataset, epochs=3, steps_per_epoch=100,\n",
    "         validation_data=val_dataset, validation_steps=3)\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4.样本权重和类权重\n",
    "“样本权重”数组是一个数字数组，用于指定批处理中每个样本在计算总损失时应具有多少权重。 它通常用于不平衡的分类问题（这个想法是为了给予很少见的类更多的权重）。 当使用的权重是1和0时，该数组可以用作损失函数的掩码（完全丢弃某些样本对总损失的贡献）。\n",
    "\n",
    "“类权重”dict是同一概念的更具体的实例：它将类索引映射到应该用于属于该类的样本的样本权重。 例如，如果类“0”比数据中的类“1”少两倍，则可以使用class_weight = {0：1.，1：0.5}。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{0: 1.0, 1: 1.0, 2: 1.0, 3: 1.0, 4: 1.0, 5: 2.0, 6: 1.0, 7: 1.0, 8: 1.0, 9: 1.0}\n",
      "Epoch 1/4\n",
      "50000/50000 [==============================] - 1s 26us/sample - loss: 0.3734 - sparse_categorical_accuracy: 0.9020\n",
      "Epoch 2/4\n",
      "50000/50000 [==============================] - 1s 24us/sample - loss: 0.1698 - sparse_categorical_accuracy: 0.9518\n",
      "Epoch 3/4\n",
      "50000/50000 [==============================] - 1s 24us/sample - loss: 0.1254 - sparse_categorical_accuracy: 0.9644\n",
      "Epoch 4/4\n",
      "50000/50000 [==============================] - 1s 25us/sample - loss: 0.1001 - sparse_categorical_accuracy: 0.9709\n",
      "Epoch 1/4\n",
      "50000/50000 [==============================] - 1s 27us/sample - loss: 0.3742 - sparse_categorical_accuracy: 0.9014\n",
      "Epoch 2/4\n",
      "50000/50000 [==============================] - 1s 24us/sample - loss: 0.1772 - sparse_categorical_accuracy: 0.9504\n",
      "Epoch 3/4\n",
      "50000/50000 [==============================] - 1s 25us/sample - loss: 0.1286 - sparse_categorical_accuracy: 0.9633\n",
      "Epoch 4/4\n",
      "50000/50000 [==============================] - 1s 25us/sample - loss: 0.1035 - sparse_categorical_accuracy: 0.9707\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<tensorflow.python.keras.callbacks.History at 0x7f7b5552ebe0>"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 增加第5类的权重\n",
    "import numpy as np\n",
    "# 类权重\n",
    "model = get_compiled_model()\n",
    "class_weight = {i:1.0 for i in range(10)}\n",
    "class_weight[5] = 2.0\n",
    "print(class_weight)\n",
    "model.fit(x_train, y_train,\n",
    "         class_weight=class_weight,\n",
    "         batch_size=64,\n",
    "         epochs=4)\n",
    "# 样本权重\n",
    "model = get_compiled_model()\n",
    "sample_weight = np.ones(shape=(len(y_train),))\n",
    "sample_weight[y_train == 5] = 2.0\n",
    "model.fit(x_train, y_train,\n",
    "         sample_weight=sample_weight,\n",
    "         batch_size=64,\n",
    "         epochs=4)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/3\n",
      "782/782 [==============================] - 2s 3ms/step - loss: 0.3669 - sparse_categorical_accuracy: 0.9025\n",
      "Epoch 2/3\n",
      "782/782 [==============================] - 2s 3ms/step - loss: 0.1709 - sparse_categorical_accuracy: 0.9536\n",
      "Epoch 3/3\n",
      "782/782 [==============================] - 2s 3ms/step - loss: 0.1252 - sparse_categorical_accuracy: 0.9659\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<tensorflow.python.keras.callbacks.History at 0x7f7b54967908>"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# tf.data数据\n",
    "model = get_compiled_model()\n",
    "\n",
    "sample_weight = np.ones(shape=(len(y_train),))\n",
    "sample_weight[y_train == 5] = 2.0\n",
    "\n",
    "train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train,\n",
    "                                                    sample_weight))\n",
    "train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64)\n",
    "\n",
    "val_dataset = tf.data.Dataset.from_tensor_slices((x_val, y_val))\n",
    "val_dataset = val_dataset.batch(64)\n",
    "\n",
    "model.fit(train_dataset, epochs=3, )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5.多输入多输出模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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nz595N+ytra+ceMG3+Lj45OVlXXixIk33niDnSOcPHlyd5YokUisrKzu3r3LJo2MjIiIf+TojBkzVq5cyX6fVImJiQobgxbi6VxERERdXd3u3burq6ubm5vZoBcisra2fvfdd7dt27Z+/Xp7e/tz586x24j/9NNP7u7u7Kfadtelf//+yi89IyNDHSshOoGBgStXrtTRu2/DUwUGBgodAhiQf//73wsWLFixYsXBgweTkpKeet92ZL3OCZv1xo4du3LlSnWsh7jk5ubu2LFDX3M68NhJGeCh/NO4ZcuW9erVKzQ09OLFizdv3ly/fn1MTIzmFsd+inR0dNTcIlSi9nju3r1rZWV19erVOXPmfPzxx++8805qaqr8DBERETt37tyxY0dQUNDo0aNZgr9//35RUVF9fT2foYmora3N2Ni4CzHo8anQF198UY/XDgC0Zvbs2S+88MI///nP06dPT5gwYe/evf/4xz80sSBkPS1kPQcHB71MDRzHkV7ndGC++OILoUMQF9z6RePa2tp+//33S5cubdu27dixY2vXrlXpjKOq7t+/T0RTp06lx2f4mpubiUgmk9XU1NDjnR0R8ecO250UPJ52cRz3z3/+09jY+K233mptbfX19WVdyfczYMCApUuX7tmzZ+fOnQsXLmSNbm5u7CJ4vqvr168nJSWpbT0BAAye/G587dq1Li4up06dSktLk0qla9as0dBCkfWQ9QBAJSj/lMJuWVZbW8smGxsbSW7P29rays9DRE1NTUTE39CZDcT//vvvv/nmmx9++CE/P1+ZPb7CElmf/BLZu2y5DN/nuXPnRo0a9fbbbxORm5sbEW3cuPHmzZsfffQRy0CnTp1qa2tzcXG5c+fOrVu32Ke++uqrfv36ff311x3F09DQQET19fXaief27dusW5bkmJqamrffftvMzMzIyOjOnTvl5eVnzpxJTU19+PAhEf3444/8I3QjIyNbWlpu3bo1ZMgQ1uLv7z9o0KANGzaEhoampqauWbNmxYoVCxYs4Nelrq7uKX8SAADomMJufNu2beyWkrNnz+7bt+8zzzzT+ceR9QhZDwC0AuXf09XX17M7dt65c2f79u03b95kN9oqLi4+e/bsqVOn2P3KYmNj79+/v2vXLjaZkJBQVVVFRGPHjq2rqwsLC/P19R0/fvywYcPs7OzYXaGVXOLmzZtZn5s2baqpqUlMTGR54v3332eFKBElJiZWVVXdvXv39u3bFy5cMDU1JaItW7aMHj16+/bt77zzzmuvvebh4REcHPzw4UOpVBoQENC3b9+ffvqJfbxnz559+/bt2bPnk8F8//33q1evZjlm8eLFx44d2717t0bjyc7OXrp0KRHdvn3b3d190qRJkyZNGjZsmI2Nzd69e318fIgoLi6ub9++sbGxLi4usbGx/fr1i4uL45/Pa2tr6+PjExoayq+FmZlZdnb266+//n//93+RkZF3795NTU01Njb+4IMP2LpERERcvXpV5Y0DAACIiEghrTQ2Nk6ZMiU+Pv4f//jHhAkTOn+kO7Iesh4AaI2EP5VlsCQSSXp6uoZuisBx3P79++/du7d69Woiamtru3379vnz5//1r3/xV413k5ub240bN8TzdxRDPPX19Z6enr/99hufGtUlIyOD3c9Nvd2KhEb/F0Bw+PtC14htvyeGLCNPDPFoLuvR47tG6eX9UcS2bYOG6PE23DX49U+z2BNXw8LC2KSxsbGjo6O3t/czzzwj6diff/4pbNi6bvfu3e+++64msiAAAHQNsp7mIOsBgPJw50/NysnJIaL//Oc/b7/9NnsI0pUrV+Lj4w8dOuTh4aGWRbArE+rq6tijZgUnYDyXLl1avHhxQ0NDW1sbDiYAAERFXb+xIOvxkPUAoAvw659mHThwYNmyZfv27XNwcBg3blxAQMDPP/+srtqvrq4uJiamrKyMiMLDw3Nzc7vfp07HY2FhUVtba2RklJaW1u4VHaAWEonEyMho1apV8fHx+fn5fHt+fv62bdvS09M9PT0lEomHhwe7dwJz9uzZ6dOnSyQSLy8v9hRpLcvLy/P397e2th4wYEBQUBC7dIeIOI5LTk4eOXJk7969PT09U1JSlD9I1VqfUql01apV7J+Lyc/Pj4+Pf/fdd9mPJ8p+CwC6TPAsI7Z4kPW0RucSnwhDYsrLy1NSUgIDAxUeMsxx3L59+wICAmJjY8PCwtLS0lg70p9GaOXh8qJGROnp6UJHAbqB7Ss11/+tW7cE7ETJ/wUicnFxUWg8f/783Llzm5ubOY5jNzcnokWLFsnPU1RURER//vln18Lrjry8vJkzZx45cuTKlSvBwcFENHnyZPbW6tWr582bl5SUFB4ebmZmRkQ7d+4UYZ9VVVVvvPFGQUGBwkecnJyU3Caxr4Ou0fR+D0QuICAgICBAc/0LmPiU37Z1MfGJMCSG3Xxo2LBh8o3r1693cnKqrq7mOK66utrJySkxMZG91f30p+ltWOdgh45DIlCBRg+DCgsLvb29BexE+fJPYa+dl5fn6OhYVVUlP8+ECROI6PDhw3xjS0sLEbFMqWWJiYn19fV8GJaWlhYWFhzH3bp168033+Rn++abb9rN8YL3yfzyyy8eHh6PHj2S/8iwYcNQ/oFGofwzcBo9dBY28alU/ulc4hNnSHxg8t9ncXGxiYlJXFwc37Jx40Zzc3P+6+1m+kP5pwCDPwFEoayszM/P7969e4J3oiqZTBYcHLxgwQJra2v59vT0dFtb28WLFxcWFrIWdif0Hj16aDM8Zvny5fI3RZBKpewO6SUlJQkJCXz7tGnTBgwYoORdebXZJ+Pp6eni4hIVFaVMVwAAIofEpwUiDOlJqampUql0ypQpfMvkyZMbGhqSk5PZJNKfeqH8A1C/mpqaqKio6OjoiIiIadOmRUREsMcf79mzhx+qXltbm5CQwE9++umn169fr6ioWLJkCcdxubm5kZGRzs7OFRUVs2bN6t+///Dhw9njIpXshEWSnZ3t4OBw4cIFza3s8ePHr169+sorryi029nZZWRk1NfXBwUFsXONynxLHMcdO3Zs8eLFDg4ODx48CAkJsba2Hj58+OXLl9mnGhsb2Q11vby8pk6deu3aNZWilclka9euTUxMTExMJCJvb29bW1v5GVpaWti5UlH1yZs+ffrevXsLCgpU6g0AQNOQ+EiUiU+EIT2J3SjRwcGBb3F0dCSiX3/9lW9B+lMnQX97FAXCgChQmjIDRWpra11dXdetW8cmKysrXV1dBw0axPangwcPlu9BfpIej4WQSqVZWVnsmrFly5ZduHAhNTWV3VMuJydHyU6Yo0eP9urV6/jx48qsnZL/CwqLCAoKIqKWlhaFediL7du3E1FkZKRCe0ffUnV1dWlpqYWFBRFt3LixuLj44MGDRDR69Gg2Z1hY2B9//MFe+/j42NjY1NTUKLN2HMcdOXKElWHOzs579+6VyWQKM+Tk5JiZmf38889Kdqj9Pq9cuUJE8iNkMPgTNA2DPw2cMgPndDTxdXnwp64kPhGGxAcg/316enoSUUNDA9/Cbqj74osv8i3dSX8Y/KkAO3QcEoEKlEkVMTExRHT79m2+5cCBA0QUFRXFPbG3kp9U2Bu6uroSUV1dHZvcsWMHEbEH1CrfCcdxra2tSq5d18o/JycnS0vLJ+dhL2Qy2ezZs4noxIkT8u2df0tDhw6V78HGxqZHjx4cx126dOnJc1hZWVlKrmB1dXVeXt6uXbt69epFRPv375d/t7W1deLEiWlpaUr2Jkif5eXlROTr68u3oPwDTUP5Z+CUOXTW0cTX5fJPVxKfCEPiA5P/PtkZz8bGRr6F3a101KhRfEt30h/KPwUY/AmgZhcvXiSiPn368C0TJ04koh9++EGlfoyMjIiInXsjohkzZhDRzZs3VY3HxESzj/esqKiwsrLq6F2JRJKSkuLm5hYSEsI/F4Ge9i3J38pZIpFYWVmxISs//fSTu7u7wl7Mz89PyVCtrKzc3d2XLVu2Z88eIvrss8/k312/fv2UKVPmzp2rZG+C9NmvXz8iqqysVKlDAACNQuKTJ6rEJ9qQ5Lm5uRHRw4cP+RY2ctje3p5vQfpTI5R/AGrGsldxcTHfMnDgQCKytLTsTrdsJ8hGw4uKsbFxW1tbJzP06dMnMzOzsbFx3rx5fGPXvqX79+8XFRWxMSG8zpfeLn9/f/rrhe9ZWVkWFhZr165VtSst98mSMaemx2cDAKgFEp8CESY+EYbEY0/Dlq9L79y5Q0Te3t58C9KfGqH8A1AzdubsxIkTfEtpaSkRTZ06lR7vv5qbm4lIJpOxx/LwuzOpVNpRt/fv3+9aJ530qRZ2dnbyZ+zocQ6QzwTu7u4pKSnffvst39L5t9QRNzc3drk533L9+vWkpCRVY2Z55dVXX2WTp0+fLi8vj46O5mdQ9Yy1dvqkxydE7ezsVO0KAEBzkPhEmPhEGFJH5s+fb2lpef78eb4lOzvb1NT0zTff5FuQ/tRJI0NKdQrhehhQmjLXCdTX13t4eDzzzDP8YPrw8PBx48axa8RnzpxJRGvWrMnPz9++fTsbPfL1119LpVIXFxdzc/OSkhL2KTaonb+A4cCBA6NGjVK1E/YD1MmTJ5VZOyX/F+ivQ/YXLlxIRLW1tXwLO4FXXl6u8MEVK1bw317n3xJ7lit/yxN2ArilpaWxsXHQoEFEtHDhwkOHDsXGxvr4+LDLzbdu3frcc891dJVdQkJCcnIyuwlBY2Ojv79/YGBgW1sbx3FnzpyZNGnSrsd27ty5YsWK2NhYsfXJsNug4dYvoE249s/AKXPdlI4mvi5f+6cTiU+EIfELJaIhQ4bIN8bHx7u6urKvtKamZsiQIevXr5efoTvpD9f+KcAOHYdEoAIlU0VtbW1UVJSPj09ERERUVNQHH3zQ1NTE3rpx48bo0aPNzc19fHxu3Ljh7e0dHBz8+eefNzU1RUdH29rafvnll2xOtl/bunXrvXv3KisrN+ymWKwAACAASURBVG/ezD/wVPlOTp8+bWdnd+7cOWXWrmvlHzuPeOrUKTaZmZnp6+tLRK+99tp3330n/8GWlpbx48c/9VvizyBu2LDh4cOH7NJ/Ilq9enVDQ0NRUdHrr79uZWU1cODARYsW3b17l/W2dOlSiURib2/fbszr1q1zcXHp16/fkiVLwsPDz5w5w/LZxYsX2e1VFBQUFIiqT97HH39sZGTEumJQ/oGmofwzcEoeOuti4uty+Sf+xCfCkJjs7OxFixYRkYmJyZYtW65evcraZTJZcnJycHBwTEzM7NmzP/nkEzWmP5R/CrBDxyERqECbh0HK79fUpWvlH8dxvr6+y5cv11hcymIHB/rdp5+fX1hYmHwLyj/QNJR/Bk6bh85aTnxdLv84vU583aShkLqT/lD+KcC1fwCgMnb1BW///v0nTpyoqKgQKh4iqq+v37lzZ3Jysh73mZubm5+fn5CQIN+o6UtcAACADCbxdZOGQkL6Uy/N3hgXALqMDY6vq6tjz70VleLi4vDwcHt7+7///e9Dhw4dOHBgZmbmypUrk5OT+Rt2a1lhYWFcXFzfvn31tc/y8vJNmzadPXuWfTY/P//IkSPV1dUFBQVqDA8AQEBIfCrRRJLqJk2EhPSndij/AESnrq4uLi6urKyMiMLDwxctWjR27Fihg/ofrr3bLj///PObNm3avXv3qlWrtB8SEY0YMUKP+2xtbT148GBaWhqfU4cOHcpuK/rhhx+qMz4AACEg8XWBJpJUN6k9JKQ/TUD5ByA6vXv3jouLi4uLEzoQ1QwePFioFKj3TE1N5Z8hAQCgZ5D4oF1If5qAa/8AAAAAAAAMAso/AAAAAAAAg4DyDwAAAAAAwCCg/AMAAAAAADAIuPULEdGlS5ckEonQUYAOuHTpEhF98cUXQgeiKfhfAIB26fF+DzrH7saplxuA3ud0YEpLSx0dHYWOQkQk7d7K1qA4OjqyXRsAgB4zMTHJzs6eMGGC0IGAjvn+++8nT56MJywDgO5auXLl9u3bhY5CLFD+AQhAIpGkp6cHBgYKHQgAAIDAkBMBtAnX/gEAAAAAABgElH8AAAAAAAAGAeUfAAAAAACAQUD5BwAAAAAAYBBQ/gEAAAAAABgElH8AAAAAAAAGAeUfAAAAAACAQUD5BwAAAAAAYBBQ/gEAAAAAABgElH8AAAAAAAAGAeUfAAAAAACAQUD5BwAAAAAAYBBQ/gEAAAAAABgElH8AAAAAAAAGAeUfAAAAAACAQUD5BwAAAAAAYBBQ/gEAAAAAABgElH8AAAAAAAAGAeUfAAAAAACAQUD5BwAAAAAAYBBQ/gEAAAAAABgElH8AAAAAAAAGAeUfAAAAAACAQUD5BwAAAAAAYBBQ/gEAAAAAABgElH8AAAAAAAAGAeUfAAAAAACAQUD5BwAAAAAAYBBQ/gEAAAAAABgElH8AAAAAAAAGAeUfAAAAAACAQUD5BwAAAAAAYBBQ/gEAAAAAABgEE6EDADAIP/744/nz5+Vbjh07VlRUxF7b29vPnz9fiLgAAAC0DTkRQEASjuOEjgFA/7377rtJSUk9e/Z88q3W1tb+/fvfu3dP+1EBAABoH3IigIAw+BNAG2bPnk1Eze0xNjYOCgoSOkAAAAAtQU4EEBB+/QPQBplMZm9vX1lZ2e67Fy9eHDdunJZDAgAAEARyIoCA8OsfgDYYGRnNnz+/R48eT75lb28/duxY7YcEAAAgCOREAAGh/APQkrlz57a0tCg09ujRIyQkRCKRCBISAACAIJATAYSCwZ8A2uPq6vrf//5XofG3334bMWKEIPEAAAAIBTkRQBD49Q9Ae4KDg01NTeVbhgwZgjwHAAAGCDkRQBAo/wC0Jzg4uLW1lZ80NTVdsGCBgPEAAAAIBTkRQBAY/AmgVSNHjvztt9/4/7ubN28OGTJE2JAAAAAEgZwIoH349Q9Aq9566y1jY2MikkgkXl5eyHMAAGCwkBMBtA/lH4BWzZ07VyaTEZGxsfFbb70ldDgAAACCQU4E0D6UfwBaZWdnN378eIlEIpPJAgIChA4HAABAMMiJANqH8g9A2+bPn89x3Msvv2xrayt0LAAAAEJCTgTQMtz6Rc0cHR3LysqEjgIA9IGJiUl2dvaECROEDgRAZd9///3kyZOlUqnQgQCAXlm5cuX27duFjkK3mQgdgL4pKytbuXLl2LFjhQ4EVJCbm7tjx46MjAytLbG6utrKykoikWhhWYGBgdgmdVRgYOCdO3eEjgKgK+7cuSOVSrW5XwW12LFjBxGtXLlSmwvVTk7Ufq4Htdu+fTt+Zek+lH/q9+KLL2L8um5hv4Hr8V8N2yQACAJ7Hp3zxRdfkJ7+4fQ+1xsCtn1CN+HaPwAAAAAAAIOA8g8AAAAAAMAgoPwDAAAAAAAwCCj/AAAAAAAADALKPwAAAAAAAIOA8g8AAAAAAMAgoPwD6LoxY8ZERUUJHYU65efnb9u2LT093dPTUyKReHh4NDQ08O+ePXt2+vTpEonEy8srPT1d++Hl5eX5+/tbW1sPGDAgKCjo9u3brJ3juOTk5JEjR/bu3dvT0zMlJYXd4FtUfUql0lWrVuGBRQCgf/QvG5KIE6IIQ2LKy8tTUlICAwMVHjXMcdy+ffsCAgJiY2PDwsLS0tJYO9KiYDhQKyJKT08XOgpQDdtLduGDc+bMWbNmjdrj4d26dav7nSi/TZ4/f37u3LnNzc0cx9XU1LBdxKJFi+TnKSoqIqI///yz+4GpKi8vb+bMmUeOHLly5UpwcDARTZ48mb21evXqefPmJSUlhYeHm5mZEdHOnTtF2GdVVdUbb7xRUFCg5CpjfwK6q8v7VRBWQEBAQECAqp/SiWyo0jYp8oQowpCYkpISIho2bJh84/r1652cnKqrqzmOq66udnJySkxMZG+pmha7tn2CAuya1QyHa7pInIcphYWF3t7e3e9HyW0yLy/P0dGxqqpK/oMTJkwgosOHD/ONLS0tRMQyopYlJibW19fzYVhaWlpYWHAcd+vWrTfffJOf7ZtvviEiFxcXsfXJ/PLLLx4eHo8ePVKmK+xPQHeJc78KTyXCw2t1ZUPlt0nxJ0RxhsQHJl/+FRcXm5iYxMXF8S0bN240Nzfnv16V0qIIt09dhMGfAGJUVlbm5+d379497SxOJpMFBwcvWLDA2tpavj09Pd3W1nbx4sWFhYWsxdTUlIh69OihncDkLV++3NzcnJ+USqWhoaFEVFJSkpCQwLdPmzZtwIABd+/eFVufjKenp4uLi/6NkgIA0AQtZ0PSkYQo2pCelJqaKpVKp0yZwrdMnjy5oaEhOTmZTSItah/KP4CuaGtry8jICAkJmThxIsdxx44dW7x4sYODw4MHD0JCQqytrYcPH3758mWO43JzcyMjI52dnSsqKmbNmtW/f//hw4dnZmYS0Z49eyQSiUQiIaLa2tqEhAR+8tNPP71+/XpFRcWSJUvYErOzsx0cHC5cuKCJ1Tl+/PjVq1dfeeUVhXY7O7uMjIz6+vqgoCB2TlFBTU1NVFRUdHR0RETEtGnTIiIiHjx40MkXwj7V2Ni4ZcuW0NBQLy+vqVOnXrt2TaVoZTLZ2rVrExMTExMTicjb29vW1lZ+hpaWFnZOVFR98qZPn753796CggKVegMAECE9y4akUwlRhCE9KScnh4gcHBz4FkdHRyL69ddf+RakRW0T8JdHvUQYrKWDujZIiR/gLpPJSktLLSwsiGjjxo3FxcUHDx4kotGjR0ul0qysLHbl2LJlyy5cuJCamtq7d28iysnJ4Thu8ODB8ouWn6S/Dp84evRor169jh8/rmqcymyTQUFBRNTS0qLwQfZi+/btRBQZGanQXltb6+rqum7dOjZZWVnp6uo6aNCg6urqjr4QNmdYWNgff/zBXvv4+NjY2NTU1Ci5OkeOHGFlmLOz8969e2UymcIMOTk5ZmZmP//8s5Idar/PK1euEJH8SJiOYH8CuguDP3VUFwbX6Uo2VHKb1JWEKMKQ+ADk/16enp5E1NDQwLfU19cT0Ysvvsi3KJ8WMfhTLbBrVjMcrumirh2myGQy+X3c0KFD+U5kMpmNjU2PHj3YpKurKxHV1dWxyR07dhDRnDlzOI4bNmyY/KLlJ+mJi6dbW1tVDZJTbpt0cnKytLR88oP86syePZuITpw4Id8eExNDRLdv3+Y/cuDAASKKioriOv5CLl269OR5qKysLCVXp7q6Oi8vb9euXb169SKi/fv3y7/b2to6ceLEtLQ0JXsTpM/y8nIi8vX1fWon2J+A7kL5p6O6cHitK9lQyW1SVxKiCEPiA5P/e7EzoY2NjXwLu1vpqFGj+Bbl0yLKP7XA4E+ALmLjUtqdlEgkVlZW/EgMIyMjImJn2ohoxowZRHTz5k1Vl2hiYtLlaDtXUVFhZWXV0bsSiSQlJcXNzS0kJIR/LgIRXbx4kYj69OnDt0ycOJGIfvjhB+r4C/npp5/c3d0V9kR+fn5KhmplZeXu7r5s2bI9e/YQ0WeffSb/7vr166dMmTJ37lwlexOkz379+hFRZWWlSh0CAIiTPmVD0qmEKNqQ5Lm5uRHRw4cP+ZYHDx4Qkb29Pd+CtKhlKP8AtI3t8tjYd5EwNjZua2vrZIY+ffpkZmY2NjbOmzePb2SJvLi4mG8ZOHAgEVlaWnbS1f3794uKitjYD17nS2+Xv78//fUC96ysLAsLi7Vr16ralZb7ZEmXU/opggAAekmE2ZB0MyGKMCSeh4cHEcnXpXfu3CEib29vvgVpUctQ/gFo2/3794lo6tSp9HiX19zcTEQymYw9yYffA0qlUvkPKkyqkZ2dnfyZOXq8r5ff47u7u6ekpHz77bd8CzuPeOLECb6ltLSUHq9aR9zc3Nhl5XzL9evXk5KSVI2Z5Y9XX32VTZ4+fbq8vDw6OpqfgZ3gFFuf9PjEp52dnapdAQDoExFmQ9KRhCjCkDoyf/58S0vL8+fP8y3Z2dmmpqZvvvkm34K0qG0aGlRqsAjX6uigrl2jUltbS0R2dnZs0snJiYj4O3ywk5rs2nF2DQN/rcKBAwdGjRrF3po5cyYRrVmzJj8/f/v27WzAyddffy2VSl1cXMzNzUtKStin2M9QJ0+eVDVOZbbJhQsXElFtbS3fwk7UlZeXK8y5YsUK/ruqr6/38PB45pln+EsLwsPDx40bx1atoy+ksbFx0KBBRLRw4cJDhw7Fxsb6+Piwy8q3bt363HPPdXSVXUJCQnJyMrtrWWNjo7+/f2BgYFtbG8dxZ86cmTRp0q7Hdu7cuWLFitjYWLH1ybDbneHWL6DfcO2fjurCtVW6kg2V3CZ1IiGKMCR+oUQ0ZMgQ+cb4+HhXV1f2ldbU1AwZMmT9+vXyMyifFnHtn1pg16xmOFzTRV04TKmrq+N/FEpISIiLi2OvN2zY8PDhQ3Y5OxGtXr26oaGBJbytW7feu3evsrJy8+bN/ONNb9y4MXr0aHNzcx8fnxs3bnh7ewcHB3/++edNTU3R0dG2trZffvklm/P06dN2dnbnzp1Tde2U2SbZ+cJTp06xyczMTF9fXyJ67bXXvvvuO/k5W1paxo8fz0/W1tZGRUX5+PhERERERUV98MEHTU1NHMfxZwrb/UKKiopef/11KyurgQMHLlq06O7du6y3pUuXSiQSe3v7doNct26di4tLv379lixZEh4efubMGZa3Ll68yG6voqCgoEBUffI+/vhjIyMj1lXnsD8B3YXyT0epenitQ9lQyW1S/AlRhCEx2dnZixYtIiITE5MtW7ZcvXqVtctksuTk5ODg4JiYmNmzZ3/yySddToso/9QCu2Y1w+GaLtL0YYrCDc20TMlt0tfXd/ny5VqIp3PsCEC/+/Tz8wsLC1NmTuxPQHeh/NNRGj28FjYbKr9N6nFC7CYNhaR8WkT5pxa49g8AiIj2799/4sSJiooKAWOor6/fuXNncnKyHveZm5ubn5+fkJCgxngAAECN9DUhdpOGQkJa1D6Uf/D/U7jQGdSIDYWvq6sTOpDODBw4MDMzc+XKlQr3+9KmwsLCuLi4ESNG6Guf5eXlmzZtOnv2bN++fdUYDwCoFxKihuhENiT9TYjdpImQkBYFgfJPn3Ecl5ycPHLkyN69e3t6eqakpHBP3FS3qalp06ZNY8eOtba2Vr7bjIwMPz+/F154Ydq0aTNmzHjnnXfi4+MjIyPVvQb/W2K7K3LmzBlfX1+JRCKRSCZNmjRp0iQvL68ZM2YkJyezu4cJrq6uLiYmpqysjIjCw8Nzc3OFjqgzzz///KZNm3bv3i1UACNGjFB7AhBPn62trQcPHkxLSxPbXc4BDER5eXlKSkpgYODYsWPbnUEnEiJ1sCIiT4i6lQ1JTxNiN6k9JKRFwQg58lQfkZiu1Vm9evW8efOSkpLCw8PNzMyIaOfOnU/O1tDQwG6xpUyfd+/effnll11cXC5dusSu3G1razt48GD//v0XLlyo5hV4rJMVYbnE2dmZTba1tR07dmzw4MFDhgz5/ffflexfv69REdU2CSrB3w50lzj3qyUlJUQ0bNiwjmYQf0Jk2l0RtSREPb62SpzbJKhEj7dPbcKvf3qrtLS0tLT00KFD77zzzkcffXT06FEi+uijj56cs1evXjY2Nsr0KZPJ/P39f/311//3//7fmDFj2FN6jIyMgoODMzMzNTRGovMVeeaZZ4ioZ8+ebNLIyGjGjBk5OTl1dXX+/v6NjY2aCAkAAHTRs88+2/kMIk+IvHZXBAkRAJSB8k9vlZSUyF9HO23atAEDBty9e7c7fR45ciQ3Nzc6OvrJgTEvv/xyQEBAdzrvSBdWxM7ObsOGDQUFBbiSGAAANEGQhNgFSIgAoADlnwDq6uo2bNgQHBwcHh7+0ksvJSYmchxHRDU1NVFRUdHR0REREdOmTYuIiGBPjj527NjixYsdHBwePHgQEhJibW09fPjwy5cvE9EXX3zRv39/iUSyZs0a1jl7dsonn3zi7e1ta2srv9yWlpYJEyaw1w0NDREREYsXL16zZs17770nf54yOzvbwcHhwoULT0Z+5MgRIpoyZUq76zVr1iz2Qpsr0pHZs2cbGRmdPn2689kAAEBA2kmIncegQwlRla/2f5AQAeAvhBx5qo/oadfqtLS0vPTSS8HBwW1tbRzHpaSkENHx48dra2tdXV3XrVvHZqusrHR1dR00aFB1dXVpaamFhQURbdy4sbi4+ODBg0TEP3dl586dRHTy5Ek2WVJSMnfu3CeXm5OTY2Zm9vPPP3Mc19raOnr06LCwMHatwn//+19jY2N+Yzh69GivXr2OHz/+ZCdeXl5E9PDhw05WUJsrwlAHF3LY2tr279+/k1B5+n09wFO3SRAt/O1AdymzXxUkISqkDN1NiO3mvu4nRD2+tkq/c72B0OPtU5tMtFNkAm/Xrl0XLlz4888/jYyMiGj+/PlE5O3tHR8ff/PmzbfffpvNZmNjs2bNmpCQkM2bN3/44YfPPPNMfn5+bGwsET377LORkZG//PILm/Ptt9/eunXrv//9b19fXyLau3dvVFSUwkKlUmlMTExKSsqoUaOI6D//+c+PP/64f/9+dq2Ci4vL4MGDb968yWb29/evra01MWln22BJsaGhwdLSsqMV1OaKdM7ExIStoJK++OIL5WfWLZcuXVLpqwAA0AJBEqIC3U2IKlEpIZaVlellQrx06RLpda43BGVlZQ4ODkJHofuErj/1DT3tbP3rr79ORHV1dQrtL730EhE9evSIbykqKiKi8ePHcxw3bNgw+T+WwuS2bdskEsl///vf5ubmdk+KrFmzZv369QoxNDQ0dNRhRxYsWEBE586d62Qeba4IQ+2d7GxubjY1NfX19X3qSnGPzwgCiBB+/QMdpcwvLYIkRPprytDdhEhK//qnUkIUz1WLAO3Cr3/dh2v/tK2yspKI+DOLPHbus7i4mG8ZOHAgEXVyWpEXFhZmYWGRlJR09OjR2bNnK7yblZVlYWGxdu1avqW8vJyI7t+/r2rwLJOx82cd0eaKdCI7O7u1tbWjqzLaJfQ/o6YQSgidpfzWC6CLtJ8Qn6SjCVElqiZEfT28xuBPPYDTE2qB8k/bPD09iWjTpk0ymYy1FBcXnzx5cuLEiUR04sQJfs7S0lIimjp16lP7tLS0DAsLS0lJSU9Pf+ONN+TfOn36dHl5eXR0NN/yww8/uLm5KSxLgVQqbbc9ODh41KhRH3300e3btxXeampq+vTTT4lImyvSUT/Nzc0xMTEjR44MDw9/6kIBAEAQWk6I7dLFhKgSJEQAUIDyT9uio6PNzc2//PLLqVOn7t69+/3339+8efMrr7yyatUqDw+PXbt23blzh825e/fucePGLVu2jIiampqIiHv8a8CjR4+IqLW1le82PDy8rq7uhRdeMDU15RvPnj0bHx8vlUqTkpKSkpJ27dq1cuXKkydPRkVFGRsbx8TEfPPNN42NjdnZ2Sx7sUEpX331Vb9+/b7++usngzc2Nj506JCZmZm3t/eRI0dYUmxoaMjOzn7ttddYEtXmirCl890yV65c8fHxefDgQWpqqnwnAAAgKtpMiAxLGW1tbXyLziXEjlaEkBABQDm49Yu2DR48+NKlS//6179+/PHHGzduBAQEfPjhh0ZGRubm5rm5uRs2bAgJCRkxYoSxsfGAAQOys7NNTU13795dUlJCRJs2bXr33Xf379/PktP777+/bt26Xr16EdGgQYPefffdpUuX8gv64YcfZsyY0djYeP78efkACgoKBg8enJ2d/d577wUEBNjY2CxatGjkyJHu7u6FhYXPPvtsz549+/btyz83VsFzzz33+++/f/zxx/v27YuMjLSwsDAxMXnttdcyMjLYs4+0uSI5OTn79+8nopKSkpdffrlnz549e/Y0NTWdM2dOSEhI79691fInAwAATdBaQmTOnz//+eefE1FxcfGHH344bdq0kSNHjhw5UocSYicrgoQIAEqScLi8RK0kEkl6enpgYKDQgYAKMjIy5syZo6//C9gmdRf+dqC79Hu/qsfYDicjI0PoQNQP26Qe0OPtU5sw+BMAAAAAAMAgoPwDAAAAAAAwCCj/AAAAAAAADALKPwD4n/z8/G3btqWnp3t6ekokEg8PD3YrOebs2bPTp0+XSCReXl7sAUpalpeX5+/vb21tPWDAgKCgIP5+6xzH7du3LyAgIDY2NiwsLC0tTYR9SqXSVatWlZWVKd8PAACIhGjzowhDYpAKxUuo5zbqK8IjtnWQph8Fe+vWLQE7UX6bPH/+/Ny5c5ubmzmOq6mpYbuIRYsWyc/Dbob+559/di2Y7sjLy5s5c+aRI0euXLkSHBxMRJMnT2ZvrV+/3snJqbq6muO46upqJyenxMREEfZZVVX1xhtvFBQUKLnK2J+A7sIjtnVU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\n",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "image_input = keras.Input(shape=(32, 32, 3), name='img_input')\n",
    "timeseries_input = keras.Input(shape=(None, 10), name='ts_input')\n",
    "\n",
    "x1 = layers.Conv2D(3, 3)(image_input)\n",
    "x1 = layers.GlobalMaxPooling2D()(x1)\n",
    "\n",
    "x2 = layers.Conv1D(3, 3)(timeseries_input)\n",
    "x2 = layers.GlobalMaxPooling1D()(x2)\n",
    "\n",
    "x = layers.concatenate([x1, x2])\n",
    "\n",
    "score_output = layers.Dense(1, name='score_output')(x)\n",
    "class_output = layers.Dense(5, activation='softmax', name='class_output')(x)\n",
    "\n",
    "model = keras.Model(inputs=[image_input, timeseries_input],\n",
    "                    outputs=[score_output, class_output])\n",
    "keras.utils.plot_model(model, 'multi_input_output_model.png'\n",
    "                       , show_shapes=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 可以为模型指定不同的loss和metrics\n",
    "model.compile(\n",
    "    optimizer=keras.optimizers.RMSprop(1e-3),\n",
    "    loss=[keras.losses.MeanSquaredError(),\n",
    "          keras.losses.CategoricalCrossentropy()])\n",
    "\n",
    "# 还可以指定loss的权重\n",
    "model.compile(\n",
    "    optimizer=keras.optimizers.RMSprop(1e-3),\n",
    "    loss={'score_output': keras.losses.MeanSquaredError(),\n",
    "          'class_output': keras.losses.CategoricalCrossentropy()},\n",
    "    metrics={'score_output': [keras.metrics.MeanAbsolutePercentageError(),\n",
    "                              keras.metrics.MeanAbsoluteError()],\n",
    "             'class_output': [keras.metrics.CategoricalAccuracy()]},\n",
    "    loss_weight={'score_output': 2., 'class_output': 1.})\n",
    "\n",
    "# 可以把不需要传播的loss置0\n",
    "model.compile(\n",
    "    optimizer=keras.optimizers.RMSprop(1e-3),\n",
    "    loss=[None, keras.losses.CategoricalCrossentropy()])\n",
    "\n",
    "# Or dict loss version\n",
    "model.compile(\n",
    "    optimizer=keras.optimizers.RMSprop(1e-3),\n",
    "    loss={'class_output': keras.losses.CategoricalCrossentropy()})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 6.使用回调\n",
    "Keras中的回调是在训练期间（在epoch开始时，batch结束时，epoch结束时等）在不同点调用的对象，可用于实现以下行为：\n",
    "\n",
    "- 在培训期间的不同时间点进行验证（超出内置的每个时期验证）\n",
    "- 定期检查模型或超过某个精度阈值\n",
    "- 在训练似乎平稳时改变模型的学习率\n",
    "- 在训练似乎平稳时对顶层进行微调\n",
    "- 在培训结束或超出某个性能阈值时发送电子邮件或即时消息通知等等。\n",
    "\n",
    "**可使用的内置回调有**\n",
    "\n",
    "- ModelCheckpoint：定期保存模型。\n",
    "- EarlyStopping：当训练不再改进验证指标时停止培训。\n",
    "- TensorBoard：定期编写可在TensorBoard中显示的模型日志（更多细节见“可视化”）。\n",
    "- CSVLogger：将丢失和指标数据流式传输到CSV文件。\n",
    "- 等等\n",
    "\n",
    "### 6.1回调使用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Train on 40000 samples, validate on 10000 samples\n",
      "Epoch 1/20\n",
      "40000/40000 [==============================] - 1s 33us/sample - loss: 0.3769 - sparse_categorical_accuracy: 0.8937 - val_loss: 0.2263 - val_sparse_categorical_accuracy: 0.9324\n",
      "Epoch 2/20\n",
      "40000/40000 [==============================] - 1s 31us/sample - loss: 0.1751 - sparse_categorical_accuracy: 0.9477 - val_loss: 0.1810 - val_sparse_categorical_accuracy: 0.9465\n",
      "Epoch 3/20\n",
      "40000/40000 [==============================] - 1s 31us/sample - loss: 0.1274 - sparse_categorical_accuracy: 0.9611 - val_loss: 0.1585 - val_sparse_categorical_accuracy: 0.9529\n",
      "Epoch 4/20\n",
      "40000/40000 [==============================] - 1s 29us/sample - loss: 0.1017 - sparse_categorical_accuracy: 0.9693 - val_loss: 0.1490 - val_sparse_categorical_accuracy: 0.9578\n",
      "Epoch 5/20\n",
      "40000/40000 [==============================] - 1s 31us/sample - loss: 0.0829 - sparse_categorical_accuracy: 0.9748 - val_loss: 0.1570 - val_sparse_categorical_accuracy: 0.9567\n",
      "Epoch 00005: early stopping\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<tensorflow.python.keras.callbacks.History at 0x7f7b3a7edeb8>"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model = get_compiled_model()\n",
    "\n",
    "callbacks = [\n",
    "    keras.callbacks.EarlyStopping(\n",
    "        # Stop training when `val_loss` is no longer improving\n",
    "        monitor='val_loss',\n",
    "        # \"no longer improving\" being defined as \"no better than 1e-2 less\"\n",
    "        min_delta=1e-2,\n",
    "        # \"no longer improving\" being further defined as \"for at least 2 epochs\"\n",
    "        patience=2,\n",
    "        verbose=1)\n",
    "]\n",
    "model.fit(x_train, y_train,\n",
    "          epochs=20,\n",
    "          batch_size=64,\n",
    "          callbacks=callbacks,\n",
    "          validation_split=0.2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Train on 40000 samples, validate on 10000 samples\n",
      "Epoch 1/3\n",
      "37824/40000 [===========================>..] - ETA: 0s - loss: 0.3671 - sparse_categorical_accuracy: 0.8974\n",
      "Epoch 00001: val_loss improved from inf to 0.22616, saving model to mymodel_1.h5\n",
      "40000/40000 [==============================] - 1s 34us/sample - loss: 0.3588 - sparse_categorical_accuracy: 0.8996 - val_loss: 0.2262 - val_sparse_categorical_accuracy: 0.9309\n",
      "Epoch 2/3\n",
      "39552/40000 [============================>.] - ETA: 0s - loss: 0.1673 - sparse_categorical_accuracy: 0.9499\n",
      "Epoch 00002: val_loss improved from 0.22616 to 0.17153, saving model to mymodel_2.h5\n",
      "40000/40000 [==============================] - 1s 29us/sample - loss: 0.1667 - sparse_categorical_accuracy: 0.9501 - val_loss: 0.1715 - val_sparse_categorical_accuracy: 0.9484\n",
      "Epoch 3/3\n",
      "39744/40000 [============================>.] - ETA: 0s - loss: 0.1217 - sparse_categorical_accuracy: 0.9629\n",
      "Epoch 00003: val_loss improved from 0.17153 to 0.15758, saving model to mymodel_3.h5\n",
      "40000/40000 [==============================] - 1s 30us/sample - loss: 0.1219 - sparse_categorical_accuracy: 0.9629 - val_loss: 0.1576 - val_sparse_categorical_accuracy: 0.9514\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<tensorflow.python.keras.callbacks.History at 0x7f7b1e14f128>"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# checkpoint模型回调\n",
    "model = get_compiled_model()\n",
    "check_callback = keras.callbacks.ModelCheckpoint(\n",
    "    filepath='mymodel_{epoch}.h5',\n",
    "    save_best_only=True,\n",
    "    monitor='val_loss',\n",
    "    verbose=1\n",
    ")\n",
    "\n",
    "model.fit(x_train, y_train,\n",
    "         epochs=3,\n",
    "         batch_size=64,\n",
    "         callbacks=[check_callback],\n",
    "         validation_split=0.2)\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 动态调整学习率\n",
    "initial_learning_rate = 0.1\n",
    "lr_schedule = keras.optimizers.schedules.ExponentialDecay(\n",
    "    initial_learning_rate,\n",
    "    decay_steps=10000,\n",
    "    decay_rate=0.96,\n",
    "    staircase=True\n",
    ")\n",
    "optimizer = keras.optimizers.RMSprop(learning_rate=lr_schedule)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Train on 40000 samples, validate on 10000 samples\n",
      "Epoch 1/5\n",
      "40000/40000 [==============================] - 1s 26us/sample - loss: 0.0577 - sparse_categorical_accuracy: 0.9824 - val_loss: 0.1433 - val_sparse_categorical_accuracy: 0.9630\n",
      "Epoch 2/5\n",
      "40000/40000 [==============================] - 1s 25us/sample - loss: 0.0509 - sparse_categorical_accuracy: 0.9843 - val_loss: 0.1445 - val_sparse_categorical_accuracy: 0.9626\n",
      "Epoch 3/5\n",
      "40000/40000 [==============================] - 1s 26us/sample - loss: 0.0442 - sparse_categorical_accuracy: 0.9865 - val_loss: 0.1384 - val_sparse_categorical_accuracy: 0.9641\n",
      "Epoch 4/5\n",
      "40000/40000 [==============================] - 1s 25us/sample - loss: 0.0379 - sparse_categorical_accuracy: 0.9883 - val_loss: 0.1453 - val_sparse_categorical_accuracy: 0.9654\n",
      "Epoch 5/5\n",
      "40000/40000 [==============================] - 1s 28us/sample - loss: 0.0337 - sparse_categorical_accuracy: 0.9898 - val_loss: 0.1508 - val_sparse_categorical_accuracy: 0.9626\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<tensorflow.python.keras.callbacks.History at 0x7f7b1cff8898>"
      ]
     },
     "execution_count": 40,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 使用tensorboard\n",
    "tensorboard_cbk = keras.callbacks.TensorBoard(log_dir='./full_path_to_your_logs')\n",
    "model.fit(x_train, y_train,\n",
    "         epochs=5,\n",
    "         batch_size=64,\n",
    "         callbacks=[tensorboard_cbk],\n",
    "         validation_split=0.2)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6.2创建自己的回调方法\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Train on 40000 samples, validate on 10000 samples\n",
      "Epoch 1/3\n",
      "39616/40000 [============================>.] - ETA: 0s - loss: 0.3804 - sparse_categorical_accuracy: 0.8905\n",
      "loss: 0.3786653545618057\n",
      "40000/40000 [==============================] - 1s 33us/sample - loss: 0.3787 - sparse_categorical_accuracy: 0.8910 - val_loss: 0.2354 - val_sparse_categorical_accuracy: 0.9290\n",
      "Epoch 2/3\n",
      "38592/40000 [===========================>..] - ETA: 0s - loss: 0.1771 - sparse_categorical_accuracy: 0.9487\n",
      "loss: 0.17603070653378963\n",
      "40000/40000 [==============================] - 1s 28us/sample - loss: 0.1760 - sparse_categorical_accuracy: 0.9490 - val_loss: 0.1762 - val_sparse_categorical_accuracy: 0.9452\n",
      "Epoch 3/3\n",
      "37952/40000 [===========================>..] - ETA: 0s - loss: 0.1291 - sparse_categorical_accuracy: 0.9605\n",
      "loss: 0.12881728459894656\n",
      "40000/40000 [==============================] - 1s 29us/sample - loss: 0.1288 - sparse_categorical_accuracy: 0.9607 - val_loss: 0.1470 - val_sparse_categorical_accuracy: 0.9537\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<tensorflow.python.keras.callbacks.History at 0x7f7b228edfd0>"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "class LossHistory(keras.callbacks.Callback):\n",
    "    def on_train_begin(self, logs):\n",
    "        self.losses = []\n",
    "    def on_epoch_end(self, batch, logs):\n",
    "        self.losses.append(logs.get('loss'))\n",
    "        print('\\nloss:',self.losses[-1])\n",
    "        \n",
    "model = get_compiled_model()\n",
    "\n",
    "callbacks = [\n",
    "    LossHistory()\n",
    "]\n",
    "model.fit(x_train, y_train,\n",
    "          epochs=3,\n",
    "          batch_size=64,\n",
    "          callbacks=callbacks,\n",
    "          validation_split=0.2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 7.自己构造训练和验证循环"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "epoch:  0\n",
      "Training loss (for one batch) at step 0: 2.3296241760253906\n",
      "Seen so far: 64 samples\n",
      "Training loss (for one batch) at step 200: 2.2373404502868652\n",
      "Seen so far: 12864 samples\n",
      "Training loss (for one batch) at step 400: 2.171706199645996\n",
      "Seen so far: 25664 samples\n",
      "Training loss (for one batch) at step 600: 2.155531167984009\n",
      "Seen so far: 38464 samples\n",
      "epoch:  1\n",
      "Training loss (for one batch) at step 0: 2.0785980224609375\n",
      "Seen so far: 64 samples\n",
      "Training loss (for one batch) at step 200: 1.9483530521392822\n",
      "Seen so far: 12864 samples\n",
      "Training loss (for one batch) at step 400: 1.8693970441818237\n",
      "Seen so far: 25664 samples\n",
      "Training loss (for one batch) at step 600: 1.8084864616394043\n",
      "Seen so far: 38464 samples\n",
      "epoch:  2\n",
      "Training loss (for one batch) at step 0: 1.7550199031829834\n",
      "Seen so far: 64 samples\n",
      "Training loss (for one batch) at step 200: 1.5734221935272217\n",
      "Seen so far: 12864 samples\n",
      "Training loss (for one batch) at step 400: 1.4772768020629883\n",
      "Seen so far: 25664 samples\n",
      "Training loss (for one batch) at step 600: 1.3974530696868896\n",
      "Seen so far: 38464 samples\n"
     ]
    }
   ],
   "source": [
    "# Get the model.\n",
    "inputs = keras.Input(shape=(784,), name='digits')\n",
    "x = layers.Dense(64, activation='relu', name='dense_1')(inputs)\n",
    "x = layers.Dense(64, activation='relu', name='dense_2')(x)\n",
    "outputs = layers.Dense(10, activation='softmax', name='predictions')(x)\n",
    "model = keras.Model(inputs=inputs, outputs=outputs)\n",
    "\n",
    "# Instantiate an optimizer.\n",
    "optimizer = keras.optimizers.SGD(learning_rate=1e-3)\n",
    "# Instantiate a loss function.\n",
    "loss_fn = keras.losses.SparseCategoricalCrossentropy()\n",
    "\n",
    "# Prepare the training dataset.\n",
    "batch_size = 64\n",
    "train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))\n",
    "train_dataset = train_dataset.shuffle(buffer_size=1024).batch(batch_size)\n",
    "\n",
    "# 自己构造循环\n",
    "for epoch in range(3):\n",
    "    print('epoch: ', epoch)\n",
    "    for step, (x_batch_train, y_batch_train) in enumerate(train_dataset):\n",
    "        # 开一个gradient tape, 计算梯度\n",
    "        with tf.GradientTape() as tape:\n",
    "            logits = model(x_batch_train)\n",
    "            \n",
    "            loss_value = loss_fn(y_batch_train, logits)\n",
    "            grads = tape.gradient(loss_value, model.trainable_variables)\n",
    "            optimizer.apply_gradients(zip(grads, model.trainable_variables))\n",
    "            \n",
    "        if step % 200 == 0:\n",
    "            print('Training loss (for one batch) at step %s: %s' % (step, float(loss_value)))\n",
    "            print('Seen so far: %s samples' % ((step + 1) * 64))\n",
    "            "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Start of epoch 0\n",
      "Training loss (for one batch) at step 0: 2.3331069946289062\n",
      "Seen so far: 64 samples\n",
      "Training loss (for one batch) at step 200: 2.2623767852783203\n",
      "Seen so far: 12864 samples\n",
      "Training loss (for one batch) at step 400: 2.247002124786377\n",
      "Seen so far: 25664 samples\n",
      "Training loss (for one batch) at step 600: 2.160977363586426\n",
      "Seen so far: 38464 samples\n",
      "Training acc over epoch: 0.18501999974250793\n",
      "Validation acc: 0.2630999982357025\n",
      "Start of epoch 1\n",
      "Training loss (for one batch) at step 0: 2.151536464691162\n",
      "Seen so far: 64 samples\n",
      "Training loss (for one batch) at step 200: 2.051269054412842\n",
      "Seen so far: 12864 samples\n",
      "Training loss (for one batch) at step 400: 2.0531749725341797\n",
      "Seen so far: 25664 samples\n",
      "Training loss (for one batch) at step 600: 1.8794834613800049\n",
      "Seen so far: 38464 samples\n",
      "Training acc over epoch: 0.3834800124168396\n",
      "Validation acc: 0.5056999921798706\n",
      "Start of epoch 2\n",
      "Training loss (for one batch) at step 0: 1.9189088344573975\n",
      "Seen so far: 64 samples\n",
      "Training loss (for one batch) at step 200: 1.7612669467926025\n",
      "Seen so far: 12864 samples\n",
      "Training loss (for one batch) at step 400: 1.7643043994903564\n",
      "Seen so far: 25664 samples\n",
      "Training loss (for one batch) at step 600: 1.4831492900848389\n",
      "Seen so far: 38464 samples\n",
      "Training acc over epoch: 0.5748999714851379\n",
      "Validation acc: 0.6765000224113464\n"
     ]
    }
   ],
   "source": [
    "# 训练并验证\n",
    "# Get model\n",
    "inputs = keras.Input(shape=(784,), name='digits')\n",
    "x = layers.Dense(64, activation='relu', name='dense_1')(inputs)\n",
    "x = layers.Dense(64, activation='relu', name='dense_2')(x)\n",
    "outputs = layers.Dense(10, activation='softmax', name='predictions')(x)\n",
    "model = keras.Model(inputs=inputs, outputs=outputs)\n",
    "\n",
    "# Instantiate an optimizer to train the model.\n",
    "optimizer = keras.optimizers.SGD(learning_rate=1e-3)\n",
    "# Instantiate a loss function.\n",
    "loss_fn = keras.losses.SparseCategoricalCrossentropy()\n",
    "\n",
    "# Prepare the metrics.\n",
    "train_acc_metric = keras.metrics.SparseCategoricalAccuracy() \n",
    "val_acc_metric = keras.metrics.SparseCategoricalAccuracy()\n",
    "\n",
    "# Prepare the training dataset.\n",
    "batch_size = 64\n",
    "train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))\n",
    "train_dataset = train_dataset.shuffle(buffer_size=1024).batch(batch_size)\n",
    "\n",
    "# Prepare the validation dataset.\n",
    "val_dataset = tf.data.Dataset.from_tensor_slices((x_val, y_val))\n",
    "val_dataset = val_dataset.batch(64)\n",
    "\n",
    "\n",
    "# Iterate over epochs.\n",
    "for epoch in range(3):\n",
    "  print('Start of epoch %d' % (epoch,))\n",
    "  \n",
    "  # Iterate over the batches of the dataset.\n",
    "  for step, (x_batch_train, y_batch_train) in enumerate(train_dataset):\n",
    "    with tf.GradientTape() as tape:\n",
    "      logits = model(x_batch_train)\n",
    "      loss_value = loss_fn(y_batch_train, logits)\n",
    "    grads = tape.gradient(loss_value, model.trainable_variables)\n",
    "    optimizer.apply_gradients(zip(grads, model.trainable_variables))\n",
    "      \n",
    "    # Update training metric.\n",
    "    train_acc_metric(y_batch_train, logits)\n",
    "\n",
    "    # Log every 200 batches.\n",
    "    if step % 200 == 0:\n",
    "        print('Training loss (for one batch) at step %s: %s' % (step, float(loss_value)))\n",
    "        print('Seen so far: %s samples' % ((step + 1) * 64))\n",
    "\n",
    "  # Display metrics at the end of each epoch.\n",
    "  train_acc = train_acc_metric.result()\n",
    "  print('Training acc over epoch: %s' % (float(train_acc),))\n",
    "  # Reset training metrics at the end of each epoch\n",
    "  train_acc_metric.reset_states()\n",
    "\n",
    "  # Run a validation loop at the end of each epoch.\n",
    "  for x_batch_val, y_batch_val in val_dataset:\n",
    "    val_logits = model(x_batch_val)\n",
    "    # Update val metrics\n",
    "    val_acc_metric(y_batch_val, val_logits)\n",
    "  val_acc = val_acc_metric.result()\n",
    "  val_acc_metric.reset_states()\n",
    "  print('Validation acc: %s' % (float(val_acc),))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[<tf.Tensor: id=1213104, shape=(), dtype=float32, numpy=5.6658335>]\n",
      "[<tf.Tensor: id=1213165, shape=(), dtype=float32, numpy=5.709879>]\n"
     ]
    }
   ],
   "source": [
    "##　添加自己构造的loss, 每次只能看到最新一次训练增加的loss\n",
    "class ActivityRegularizationLayer(layers.Layer):\n",
    "  \n",
    "  def call(self, inputs):\n",
    "    self.add_loss(1e-2 * tf.reduce_sum(inputs))\n",
    "    return inputs\n",
    "  \n",
    "inputs = keras.Input(shape=(784,), name='digits')\n",
    "x = layers.Dense(64, activation='relu', name='dense_1')(inputs)\n",
    "# Insert activity regularization as a layer\n",
    "x = ActivityRegularizationLayer()(x)\n",
    "x = layers.Dense(64, activation='relu', name='dense_2')(x)\n",
    "outputs = layers.Dense(10, activation='softmax', name='predictions')(x)\n",
    "\n",
    "model = keras.Model(inputs=inputs, outputs=outputs)\n",
    "logits = model(x_train[:64])\n",
    "print(model.losses)\n",
    "logits = model(x_train[:64])\n",
    "logits = model(x_train[64: 128])\n",
    "logits = model(x_train[128: 192])\n",
    "print(model.losses)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Start of epoch 0\n",
      "Training loss (for one batch) at step 0: 8.212984085083008\n",
      "Seen so far: 64 samples\n",
      "Training loss (for one batch) at step 200: 2.4881486892700195\n",
      "Seen so far: 12864 samples\n",
      "Training loss (for one batch) at step 400: 2.4067013263702393\n",
      "Seen so far: 25664 samples\n",
      "Training loss (for one batch) at step 600: 2.352705240249634\n",
      "Seen so far: 38464 samples\n",
      "Start of epoch 1\n",
      "Training loss (for one batch) at step 0: 2.3271288871765137\n",
      "Seen so far: 64 samples\n",
      "Training loss (for one batch) at step 200: 2.319481611251831\n",
      "Seen so far: 12864 samples\n",
      "Training loss (for one batch) at step 400: 2.33414363861084\n",
      "Seen so far: 25664 samples\n",
      "Training loss (for one batch) at step 600: 2.3231465816497803\n",
      "Seen so far: 38464 samples\n",
      "Start of epoch 2\n",
      "Training loss (for one batch) at step 0: 2.310762882232666\n",
      "Seen so far: 64 samples\n",
      "Training loss (for one batch) at step 200: 2.3086767196655273\n",
      "Seen so far: 12864 samples\n",
      "Training loss (for one batch) at step 400: 2.323223352432251\n",
      "Seen so far: 25664 samples\n",
      "Training loss (for one batch) at step 600: 2.3157637119293213\n",
      "Seen so far: 38464 samples\n"
     ]
    }
   ],
   "source": [
    "# 将loss添加进求导中\n",
    "optimizer = keras.optimizers.SGD(learning_rate=1e-3)\n",
    "\n",
    "for epoch in range(3):\n",
    "  print('Start of epoch %d' % (epoch,))\n",
    "\n",
    "  for step, (x_batch_train, y_batch_train) in enumerate(train_dataset):\n",
    "    with tf.GradientTape() as tape:\n",
    "      logits = model(x_batch_train)\n",
    "      loss_value = loss_fn(y_batch_train, logits)\n",
    "\n",
    "      # Add extra losses created during this forward pass:\n",
    "      loss_value += sum(model.losses)\n",
    "      \n",
    "    grads = tape.gradient(loss_value, model.trainable_variables)\n",
    "    optimizer.apply_gradients(zip(grads, model.trainable_variables))\n",
    "\n",
    "    # Log every 200 batches.\n",
    "    if step % 200 == 0:\n",
    "        print('Training loss (for one batch) at step %s: %s' % (step, float(loss_value)))\n",
    "        print('Seen so far: %s samples' % ((step + 1) * 64))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
